AFNetworking 踩坑

最近公司的项目把网络库从ASIHTTPRequest 全部替换成了AFNetworking,但是在iOS 7上遇到了频率很高的crash。具体崩溃在AFURLSessionManager.h里的[self.mutableData appendData:data];这一行

1
2
3
4
5
6
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
[self.mutableData appendData:data];
}

打印的log

1
2
malloc: *** error for object 0x633c000: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug

意思应该是mutableData在某处被释放了,于是查找所有用到mutableData的地方

1
2
3
4
5
6
7
8
9
10
11
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error{
...
NSData *data = nil;
if (self.mutableData) {
data = [self.mutableData copy];
self.mutableData = nil;
}
...
}

在这个回调里mutableData 被释放掉了,但是在iOS 7以上的系统却没发现崩溃,应该是iOS 7里,同一个task,这两个方法是在两个线程异步执行的,导致mutableData 提前置为nil。

屏幕快照 2017-01-19 下午2.51.58

解决办法

在操作mutableData的地方加锁,并且加了版本判断,崩溃就基本不会复现了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
@synchronized (self.mutableData) {
if (!self.mutableData) {
self.mutableData = [NSMutableData data];
}
[self.mutableData appendData:data];
}
}else{
[self.mutableData appendData:data];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
...
NSData *data = nil;
if (self.mutableData) {
data = [self.mutableData copy];
//We no longer need the reference, so nil it out to gain back some memory.
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {//加锁,防止崩溃在[self.mutableData appendData:data];
@synchronized (self.mutableData) {
self.mutableData = nil;
}
}else{
self.mutableData = nil;
}
}
...
}